package com.yy.young.ums.web;

import com.yy.young.common.config.DomainHandler;
import com.yy.young.common.util.ClientInfoUtil;
import com.yy.young.interfaces.log.annotation.Log;
import com.yy.young.interfaces.model.User;
import com.yy.young.base.util.GlobalConstants;
import com.yy.young.common.util.CommonUtil;
import com.yy.young.common.util.StringUtils;
import com.yy.young.interfaces.ums.model.SsoVerifyDTO;
import com.yy.young.ums.model.Config;
import com.yy.young.ums.model.SsoInfo;
import com.yy.young.ums.service.*;
import com.yy.young.ums.util.SecureHandler;
import com.yy.young.ums.util.UmsConfigHelper;
import com.yy.young.ums.util.UmsConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 单点登录服务
 * Created by rookie on 2017/5/10.
 */
@Controller
public class UmsSsoController {

    Logger logger = LoggerFactory.getLogger(UmsSsoController.class);

    @Resource(name="umsUserService")
    IUmsUserService userService;

    @Resource(name="umsDeptService")
    IUmsDeptService deptService;

    @Resource(name="umsRoleService")
    IUmsRoleService roleService;

    @Resource(name="umsLGLogService")
    IUmsLGLogService lgLogService;


    /**
     * 获取token签名
     * 主要供外部系统使用
     * @param request
     * @return
     * @throws Exception
     */
    @Log("查询token签名")
    @RequestMapping(value = "/token")
    @ResponseBody
    public Object getToken(HttpServletRequest request) throws Exception {
        Cookie[] cookies = request.getCookies();
        String token = "";
        for(int i = 0; i < cookies.length; i++){
            Cookie cookie = cookies[i];
            if (UmsConstants.SSO.TOKEN_KEY.equals(cookie.getName())){
                token = cookie.getValue();
                break;
            }
        }
        logger.debug("[获取cookie中的token信息] token=" + token);
        String callback = request.getParameter("callback");
        String jsonStr = "{\"token\":\"" + token + "\"}";
        //判断是否跨域请求
        if (StringUtils.isBlank(callback)){
            return jsonStr;
        }else{
            return callback + "(" + jsonStr + ")";
        }
    }

    /**
     * 单点验证
     * 用于单点客户端向服务端校验token令牌
     * @param request
     * @return
     * @throws Exception
     */
    @Log("单点登录token验证")
    @RequestMapping(value = "/verifySSO",method = RequestMethod.POST)
    @ResponseBody
    public Object verifySSO(HttpServletRequest request) throws Exception {
        String token = request.getParameter("token");//token令牌信息
        //令牌非空
        if (StringUtils.isBlank(token)){
            return SsoVerifyDTO.getFailInstance(token, "令牌无效");
        }
        //令牌有效性验证
        if (!SecureHandler.verifyToken(token)){
            return SsoVerifyDTO.getFailInstance(token, "令牌无效");
        }
        String userId = SecureHandler.getUserIdFromToken(token);
        User user = userService.getUserById(userId);
        user.setPassword("");//置空密码
        userService.perfectLoginUserInfo(user);//填充用户单位信息
        return SsoVerifyDTO.getSuccessInstance(token, user);
    }

    /**
     * 登录,GET请求,主要处理跳转
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/login",method = RequestMethod.GET)
    @ResponseBody
    public Object loginOfGet(HttpServletRequest request,HttpServletResponse response) throws Exception {
        if (CommonUtil.getLoginUser(request) != null) {
            logger.info("[SSO单点登录(GET)] 检测到该会话内已存在登录用户,重定向到首页！");
            String redirectTo = request.getParameter("redirectTo");
            //过滤掉注销地址
            if(redirectTo != null && redirectTo.indexOf("logout.action") > -1){
                logger.info("[SSO单点登录(GET)] 过滤掉redirectTo参数:{}", redirectTo);
                redirectTo = "";
            }
            redirectTo = CommonUtil.getDefaultValue(redirectTo, CommonUtil.getDefaultValue(DomainHandler.get(GlobalConstants.URL_CONFIG_KEY.URL_INDEX), DomainHandler.getAndFormat(GlobalConstants.URL_CONFIG_KEY.URL_PORTAL, UmsConstants.SSO.REDIRECT_INDEX_URI_DEF)));
            return new ModelAndView("redirect:" + redirectTo);
        }

        return new ModelAndView(UmsConstants.SSO.SSO_LOGIN);
    }
    /**
     * 登录,POST请求,实际登录
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public Object login(HttpServletRequest request,HttpServletResponse response) throws Exception {
        String token = null;
        if (CommonUtil.getLoginUser(request) != null) {
            token = CommonUtil.getLoginUser(request).getToken();
            logger.info("[SSO单点登录] 检测到该会话内已存在登录用户,跳过验证！");
        } else {
            String account = request.getParameter("account");//用户名
            String pwd = request.getParameter("password");//密码
            if (StringUtils.isNotBlank(account) && StringUtils.isNotBlank(pwd)) {
                //对用户的IP进行过滤
                String blacklist = UmsConfigHelper.getConfigValue(UmsConstants.CONFIG_KEY.LOGIN_IP_BLACK_LIST);//从配置项中拉取黑名单
                if (blacklist != null){
                    if (!this.verifyIp(ClientInfoUtil.getClientIpAddr(request), blacklist)){//ip验证不通过
                        lgLogService.writeLGLog(account, "0", "黑名单IP", request);//记录登录日志
                        return new SsoInfo(-1,"登录失败，请联系管理员!");
                    }
                }

                User user = userService.getUserByAccount(account);
                if(user == null){
                    lgLogService.writeLGLog(account, "0", "用户名不存在", request);//记录登录日志
                    return new SsoInfo(-1,"用户名不存在!");
                }
                if(!verifyUser(account,pwd,user)){//验证用户
                    logger.info("[SSO单点登录] 用户名或密码错误,account={},password={}", account, pwd);
                    lgLogService.writeLGLog(account, "0", "用户名或密码错误", request);//记录登录日志
                    return new SsoInfo();
                }else{
                    //验证成功,存入session
                    user.setPassword("");//清空密码
                    fillUser(user);//查询登录用户的详细信息
                    //为用户发放令牌,令牌信息通过SecureHandler类生成
                    user.setToken(SecureHandler.createToken(user.getId(), System.currentTimeMillis()));
                    logger.info("[用户信息打印] {}" ,user);
                    request.getSession().setAttribute(GlobalConstants.SESSION.KEY_LOGINUSER,user);//存入session
                    //cookie
                    Cookie cookie = new Cookie(UmsConstants.SSO.TOKEN_KEY, user.getToken());

                    //必须设置域,这样可以将cookie共享到各个子系统
                    cookie.setDomain(CommonUtil.getDefaultValue(UmsConfigHelper.getConfigValue(UmsConstants.CONFIG_KEY.SSO_COOKIE_DOMAIN), UmsConstants.SSO.DEFAULT_SSO_COOKIE_DOMAIN));//设置cookie的域,用于前台cookie共享
                    response.addCookie(cookie);//写入cookie

                    lgLogService.writeLGLog(account, "1", "成功", request);//记录登录日志

                    token = user.getToken();
                }
            }else{
                logger.info("[SSO单点登录] 用户名或密码无效,account={},password={}", account, pwd);
                lgLogService.writeLGLog(account, "0", "用户名或密码无效", request);//记录登录日志
                return new SsoInfo(-1,"用户名或密码无效!");
            }
            logger.info("[SSO单点登录] 用户[" + account + "]登录验证通过！");
        }
        /**
         * 获取重定向页面
         * 1.从redirectTo参数获取;
         * 2.默认跳转首页;
         */
        String redirectTo = request.getParameter("redirectTo");
        //过滤掉注销地址
        if(redirectTo != null && redirectTo.indexOf("logout.action") > -1){
            logger.info("[SSO单点登录] 过滤掉redirectTo参数:{}", redirectTo);
            redirectTo = "";
        }
        redirectTo = CommonUtil.getDefaultValue(redirectTo, CommonUtil.getDefaultValue(DomainHandler.get(GlobalConstants.URL_CONFIG_KEY.URL_INDEX), DomainHandler.getAndFormat(GlobalConstants.URL_CONFIG_KEY.URL_PORTAL, request.getContextPath()+"/"+UmsConstants.SSO.REDIRECT_INDEX_URI_DEF)));
        return new SsoInfo(1, "登录成功!", redirectTo, token);
    }


    /**
     * 注销登录
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) throws Exception{
        if(CommonUtil.getLoginUser(request) != null){
            String name = CommonUtil.getLoginUser(request).getName();
            String loginName = CommonUtil.getLoginUser(request).getAccount();
            logger.info("------------- " + name + "[账号" + loginName + "] 退出系统-------------");
        }else{
            logger.info("------------- SESSION 丢失-------------");
        }
        //清空session
        request.getSession().invalidate();
        //清除token信息
        Cookie cookie = new Cookie(UmsConstants.SSO.TOKEN_KEY, null);
        cookie.setDomain(CommonUtil.getDefaultValue(UmsConfigHelper.getConfigValue(UmsConstants.CONFIG_KEY.SSO_COOKIE_DOMAIN), UmsConstants.SSO.DEFAULT_SSO_COOKIE_DOMAIN));
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        return "redirect:" + UmsConstants.SSO.REDIRECT_LOGIN;
    }


    /**
     * 验证用户名密码
     * @param account
     * @param password
     * @return
     */
    private boolean verifyUser(String account,String password,User user){
        try {
            if(user != null && "1".equals(user.getState()) && SecureHandler.encrypt(password).equals(user.getPassword()) && account.equals(user.getAccount())){//密码匹配验证
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 查询登录用户的详细信息
     * @param user
     * @throws Exception
     */
    private void fillUser(User user) throws Exception{
        userService.perfectLoginUserInfo(user);
    }

    /**
     * 验证ip
     * @param ip 待验证的ip
     * @param blacklist 黑名单
     * @return
     */
    private boolean verifyIp(String ip, String blacklist){
        if (StringUtils.isNotBlank(ip) && StringUtils.isNotBlank(blacklist)){
            String[] ips = blacklist.split(";");
            if (ips != null && ips.length > 0){
                for (String black : ips){
                    if (StringUtils.isNotBlank(black)){
                        String[] blackItems = black.split("\\.");
                        String[] ipItems = ip.split("\\.");
                        if (blackItems.length != 0 && blackItems.length == ipItems.length){//ip格式相同
                            boolean flag = true;
                            for (int i=0;i<blackItems.length;i++){
                                if ("*".equals(blackItems[i]) || StringUtils.equals(blackItems[i], ipItems[i])){//通配符配置，该段忽略//该段相等，则继续下一段匹配
                                    continue;
                                } else {//不相等，则跳出匹配，进行下一个黑ip的匹配
                                    flag = false;
                                    break;
                                }
                            }
                            if (flag){//匹配成功，说明当前IP为黑名单中的IP
                                logger.warn("[SSO单点登录] IP验证失败，可疑的用户IP：{}，对应黑名单IP为：{}", ip, black);
                                //返回验证失败
                                return false;
                            }
                        }
                    }
                }
            }
        }
        return true;
    }
}

